﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.BusinessData.Infrastructure;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.BusinessData.Runtime;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.BusinessData.SharedService;
using Microsoft.SharePoint.BusinessData.Administration;
using Microsoft.SharePoint.BusinessData.Infrastructure;

namespace Office365.Helper.BCSModeler
{
    public class BdcController
    {
        private string _siteUrl;
        private BdcService _service;
        private SPSite _site;
        private SPServiceContext _context;
        private AdministrationMetadataCatalog _catalog;

        private BdcController()
        {
        }

        public BdcController(string siteUrl)
        {
            _siteUrl = siteUrl;
            
            Init();
        }

        /// <summary>
        /// Creates the BusinessDataCatalog model
        /// </summary>
        /// <param name="modelName">The name of the model.</param>
        /// <param name="isCached">Cache the model.</param>
        /// <returns>The BusinesDataCatalog model.</returns>
        public Model CreateModel(string name, bool isCached = true)
        {
            return Model.Create(name, isCached, _catalog);
        }

        /// <summary>
        /// Creates a LobSystem
        /// </summary>
        /// <param name="model">The BusinessDataCatalog model.</param>
        /// <param name="name">The name of the LobSystem.</param>
        /// <param name="systemType">The type of the LobSystem.</param>
        /// <param name="isCached">Cache the LobSystem.</param>
        /// <returns>The new created LobSystem.</returns>
        public LobSystem CreateLobSystem(ref Model model, string name, SystemType systemType, bool isCached = true)
        {
            return model.OwnedReferencedLobSystems.Create(name, isCached, systemType);
        }

        /// <summary>
        /// Creates a LobSystemInstance
        /// </summary>
        /// <param name="lobSystem">A LobSystem object.</param>
        /// <param name="name">The name of the LobSystemInstance.</param>
        /// <param name="isCached">Cache the LobSystemInstance.</param>
        /// <returns>The new created LobSystemInstance.</returns>
        public LobSystemInstance CreateLobSystemInstance(LobSystem lobSystem, string name, bool isCached = true)
        {
            return lobSystem.LobSystemInstances.Create(name, isCached);
        }

        /// <summary>
        /// Adds a property to the LobSystem
        /// </summary>
        /// <param name="instance">A LobSystem object.</param>
        /// <param name="name">The parameter name.</param>
        /// <param name="value">The parameter value.</param>
        public void AddProperty(LobSystem lobSystem, string name, object value)
        {
            lobSystem.Properties.Add(name, value);
        }

        /// <summary>
        /// Adds a property to the LobSystemInstance
        /// </summary>
        /// <param name="instance">A LobSystemInstance object.</param>
        /// <param name="name">The parameter name.</param>
        /// <param name="value">The parameter value.</param>
        public void AddProperty(LobSystemInstance instance, string name, object value)
        {
            instance.Properties.Add(name, value);
        }

        public void GenerateProxy(ref LobSystem lobSystem)
        {
            using (new SPServiceContextScope(_context))
            {
                lobSystem.GenerateProxyAssembly();
            }
        }

        /// <summary>
        /// Creates a new entity in the LobSystem.
        /// </summary>
        /// <param name="name">The name of the entity.</param>
        /// <param name="namespace">The namespace of the entity.</param>
        /// <param name="version">The version of the entity.</param>
        /// <param name="estimatedInstanceCount">The estimated count of instances.</param>
        /// <param name="cacheUsage">The cache usage.</param>
        /// <param name="lobSystem">A LobSystem object.</param>
        /// <param name="model">A BusinessDataCatalog model object.</param>
        /// <param name="isCached">Cache the entity.</param>
        /// <returns>The new created Entity.</returns>
        public Entity AddEntity(string name, string @namespace, string version, uint estimatedInstanceCount, CacheUsage cacheUsage, LobSystem lobSystem, Model model, bool isCached = true)
        {
            return Entity.Create(name, @namespace, isCached, new Version(version), estimatedInstanceCount, cacheUsage, lobSystem, model, _catalog);
        }

        /// <summary>
        /// Creates an identifier in the entity.
        /// </summary>
        /// <param name="entity">A BusinessDataCatalog entity object.</param>
        /// <param name="name">The name of the field that is the identifier.</param>
        /// <param name="identifierType">The data type of the identifier.</param>
        /// <param name="isCached">Cache the identifier.</param>
        public void CreateIdentifier(ref Entity entity, string name, Type identifierType, bool isCached = true)
        {
            entity.Identifiers.Create(name, isCached, identifierType.ToString());
        }

        /// <summary>
        /// Validates and activates the entity in the BusinessDataCatalog.
        /// </summary>
        /// <param name="entity">A BusinessDataCatalog entity object.</param>
        public void ActivateEntity(Entity entity)
        {
            ActivationError[] errors = entity.Validate();
            if (!errors.Any())
                entity.Activate();
            else
            {
                StringBuilder errorMsg = new StringBuilder();
                errorMsg.AppendLine("Could not activate entity '" + entity.Name + "':");
                foreach (ActivationError error in errors)
                    errorMsg.AppendLine(error.ToString());
                
                throw new Exception(errorMsg.ToString());
            }
        }

        public void AddAccessControlEntry(ref Model model, string identity, BdcRights rights, bool copyAcrossChildren = false)
        {
            IAccessControlList acl = model.GetAccessControlList();
            IAccessControlEntry ace = new IndividualAccessControlEntry(identity, rights);
            acl.Add(ace);
            model.SetAccessControlList(acl);
                
            if (copyAcrossChildren) model.CopyAclAcrossChildren();
        }

        public void AddAccessControlEntry(ref LobSystem lobSystem, string identity, BdcRights rights, bool copyAcrossChildren = false)
        {
            IAccessControlList acl = lobSystem.GetAccessControlList();
            IAccessControlEntry ace = new IndividualAccessControlEntry(identity, rights);
            acl.Add(ace);
            lobSystem.SetAccessControlList(acl);

            if (copyAcrossChildren) lobSystem.CopyAclAcrossChildren();
        }

        public void AddAccessControlEntry(ref Entity entity, string identity, BdcRights rights, bool copyAcrossChildren = false)
        {
            IAccessControlList acl = entity.GetAccessControlList();
            IAccessControlEntry ace = new IndividualAccessControlEntry(BdcAccessControlList.TranslateFriendlyStringToEncodedClaim(identity), rights);
            acl.Add(ace);
            entity.SetAccessControlList(acl);
            
            if (copyAcrossChildren) entity.CopyAclAcrossChildren();
        }

        public void AddAccessControlEntry(ref Method method, string identity, BdcRights rights, bool copyAcrossChildren = false)
        {
            IAccessControlList acl = method.GetAccessControlList();
            IAccessControlEntry ace = new IndividualAccessControlEntry(identity, rights);
            acl.Add(ace);
            method.SetAccessControlList(acl);

            if (copyAcrossChildren) method.CopyAclAcrossChildren();
        }

        public Method CreateMethod(Entity entity, string methodName, bool isCached, bool isStatic, string lobName)
        {
            return entity.Methods.Create(methodName, isCached, isStatic, lobName);
        }

        public Parameter AddParameters(ref Method method, string parameterName, bool isCached, DirectionType direction)
        {
            return method.Parameters.Create(parameterName, isCached, direction);
        }

        public TypeDescriptor AddTypeDescriptor(ref Parameter parameter, string name, bool isCached, string typeName, string lobName, IdentifierReference identifierReference, FilterDescriptor filterDescriptor, TypeDescriptorFlags flags, AssociationReference associationReference)
        {
            return parameter.CreateRootTypeDescriptor(name, isCached, typeName, lobName, identifierReference, filterDescriptor, flags, associationReference, _catalog);
        }

        public TypeDescriptor AddTypeDescriptor(ref TypeDescriptor typeDescriptor, string name, bool isCached, Type type, string lobName, IdentifierReference identifierReference, FilterDescriptor filterDescriptor, TypeDescriptorFlags flags, AssociationReference associationReference)
        {
            return typeDescriptor.ChildTypeDescriptors.Create(name, isCached, type.ToString(), lobName, identifierReference, filterDescriptor, flags, associationReference);
        }

        public IdentifierReference GetIdentifierReference(string name, Entity entity)
        {
            EntityReference entityRef = new EntityReference(entity.Namespace, entity.Name, _catalog);
            return new IdentifierReference(name, entityRef, _catalog);
        }

        private void Init()
        {
            // Get the Catalog for the SharePoint site
            //_service = SPFarm.Local.Services.GetValue<BdcService>(String.Empty);
            _service = SPFarm.Open(@"Data Source=WINSRV\SharePoint;Initial Catalog=SharePoint_Config_4c55beea-0427-42ac-99a1-49c9b275fb94;Integrated Security=True;Enlist=False;Connect Timeout=15").Services.GetValue<BdcService>(String.Empty);

            _site = new SPSite(_siteUrl);
            _context = SPServiceContext.GetContext(_site);
            _catalog = _service.GetAdministrationMetadataCatalog(_context);
        }

    }
}
